'use strict';


const Command = require('@imooc-cli-dev/command')
const { SpinnerFn, SpawnAsync, exec } = require('@imooc-cli-dev/utils')
const log = require('@imooc-cli-dev/log');
const fs =require('fs');
const inquirer = require('inquirer')
const fse = require('fs-extra')
const semver = require('semver');
const { handleGetTemplate } = require('./api')
const { homedir } = require('os')
const Package = require('@imooc-my-test1/package');
const ejs = require('ejs');

const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'

const TEMPLATE_TYPE_NORMAL = 'normal';
const TEMPLATE_TYPE_CUSTOM = 'custom';

const WHITE_COMMAND = ['npm', 'cnpm', 'pnpm'];
const nameReg = /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/

let userHome;

class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || '';
    this.force = !!this._argv[1].force;
    log.verbose('projectName', this.projectName)
    log.verbose('force', this.force)
  }
  async exec() {
    try {
    // 1. 准备阶段
    this.projectInfo = await this.prepare();
    // 2. 下载模版
    if(this.projectInfo) {
      await this.downloadTemplate()

      this.installTemplate()
    }
    // 3. 安装模版
    } catch (error) {
      log.error(error.message)
    }
  }

  async installTemplate() {
    if(this.templateInfo) {
      if(!this.templateInfo.type) {
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL
      }

      console.log(this.templateInfo.type, 'jjjjj')

      if(this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
        await this.installNormalTemplate()
      } else if(this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
        await this.installCustomTemplate()
      } else {
        throw new Error('无法识别的模版类型')
      }

    } else {
      throw new Error('项目信息不存在')
    }
  }

  async installNormalTemplate() {
    const Spinner = SpinnerFn('正在拷贝模版...');
    try {
      const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
      const targetPath = process.cwd();
      fse.ensureDirSync(templatePath)
      fse.ensureDirSync(targetPath)
      fse.copySync(templatePath, targetPath);

    } catch (error) {
      throw error
    } finally {
      Spinner.stop(true);
      log.success('模版安装成功')
    }

    // const ignore = ['node_modules/**', 'public/**', 'README.md', '**/*.{png,jpeg,svg,gif}', 'bin/**', 'build/**'];
    const defaultIgnore = ['node_modules/**']
    const templateIgnore = this.templateInfo.ignore || []
    const ignore = [...defaultIgnore,...templateIgnore ]
    await this.ejsRender(ignore)
    const {installCommand, startCommand } =  this.templateInfo;
    await this.execCommand(installCommand, '依赖安装过程中失败')
    await  this.execCommand(startCommand, '依赖启动过程中失败')
  }
  async execCommand(commandTxt, errTxt) {
    if(commandTxt) {
      const commandTxtArr =commandTxt.split(' ')
      if(commandTxtArr.length > 0) {
        const cmd = commandTxtArr[0];
        this.checkCommand(cmd)
        const args = commandTxtArr.slice(1);
        const ret = await SpawnAsync(cmd, args, {
          cwd: process.cwd(),
          stdio: 'inherit',
        })
        if(ret !== 0) {
          throw new Error(errTxt)
        }
      }
    }
  }
  checkCommand(cmd) {
    if(!WHITE_COMMAND.includes(cmd)) {
      throw new Error('配置的命令不可执行')
    }
  }

  async ejsRender(ignore) {
    const projectInfo = this.projectInfo;
    return new Promise(function(resolve, reject) {
      // try {
        const glob = require('glob');
        glob("**", {
          cwd: process.cwd(),
          ignore: ignore || [],
          nodir: true
        }, (err, files) => {
          if (err) {
            return reject(err);
          }
          Promise.all(files.map(file => {
            return new Promise((resolve1, reject1) => {
              ejs.renderFile(file, projectInfo, (err, result) => {
                if(err) {
                  reject1(err)
                } else {
                  resolve1([file,result ])
                }
              })
            })
          })).then((filesRes) => {
            filesRes.map((resArr, index) => {
              const [file ,result] = resArr;
                fse.writeFileSync(file,result)
            })
            resolve()
          })
        })
    })
  }
  
  async installCustomTemplate() {
    if(await this.templateNpm.exists()) {
      const rootFile = this.templateNpm.getRootFilePath();
      if(fs.existsSync(rootFile)) {
        log.notice('开始执行自定义模版');
        const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
        const options = {
          projectInfo: this.projectInfo,
          templateInfo:this.templateInfo,
          sourcePath: templatePath,
          targetPath: process.cwd()
        }
        const code = `require('${rootFile}')(${JSON.stringify(options)})`
        log.verbose('code', code)
        await this.execAsync(code)

        log.success('自定义模板执行成功');
      }
    } else {
      throw new Error('入口文件不存在！')
    }
  }

  execAsync(code) {
    return new Promise((resolve, reject) => {
      const p = exec('node', ['-e', code], {'stdio': 'inherit'})
      p.on('error', e => reject(e))
      p.on('exit', c => resolve(c))
    })
  }

  async downloadTemplate() {
    // 1. 通过项目模版api获取项目模版信息
    // 1.1 通过egg.js 搭建一套后端系统
    // 1.2 通过npm储存项目模板
    // 1.3 将项目模版存储到mongodb数据库中
    // 1.4 通过egg.js获取mongodb中的数据并且通过API返回
    const {templateName } = this.projectInfo
    this.templateInfo = this.template.find(item => item.npmName === templateName)
    userHome = homedir();
    const targetPath = path.resolve(userHome, '.imooc-test1-env', 'template')
    const storePath = path.resolve(userHome, '.imooc-test1-env', 'template', 'node_modules')
    const packageName = templateName;
    const packageNameVersion = this.templateInfo.version;
    const npmPkg = new Package({
      targetPath,
      storePath,
      packageName,
      packageNameVersion,
    })
    console.log(
      targetPath,
      storePath,
      packageName,
      packageNameVersion,
    )
    if(await npmPkg.exists()) {
      const Spinner = SpinnerFn('正在更新模版..')
      try {
        await npmPkg.update()
        this.templateNpm = npmPkg;
      } catch (error) {
        throw new Error(error.message)
      } finally {
        Spinner.stop(true)
      }

      log.success('模版更新成功')
      
    } else {
      const Spinner = SpinnerFn('正在下载模版..')
      // await Sleep(3000)
      try {
        await npmPkg.install()
        this.templateNpm = npmPkg;
      } catch (error) {
        throw new Error(error.message)
      } finally {
        Spinner.stop(true)
      }
      log.success('模版下载成功')
    }
  }

  async prepare() { 
    const templateArr = await handleGetTemplate()
    if(!templateArr || templateArr.length === 0) {
      throw new Error('项目模版不存在')
    }
    this.template = templateArr;

    // 1. 判断文件是否为空
    const nowPath = process.cwd();
    if(!this.isCwdEmpty(nowPath)) {
      let ifContinue = false;
      if(!this.force) {
        let confirmRes = await inquirer.prompt([
          /* Pass your questions in here */{
          type: 'confirm',
          name: 'ifContinue',
          default: false,
          message: '当前文件夹不为空，是否继续创建项目？'}
        ])
        ifContinue = confirmRes.ifContinue
        if(!ifContinue) return
      }
      if(ifContinue || this.force) {
        const { confirmDelete } = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'confirmDelete',
            default: false,
            message: '是否确认清空当前文件夹？'
          }
        ])

        if(confirmDelete) {
          // 清空当前文件夹
          fse.emptyDirSync(nowPath)
        }
        // 不清空也要创建
      }
    }
    return this.getProjectInfo()

  }

  async getProjectInfo() {
    let projectInfo = {}
    // 3. 选择创建项目或者组件
    const { type } = await inquirer.prompt([
      {
        type: 'list',
        name: 'type',
        message: '请选择初始化类型',
        default: TYPE_PROJECT,
        choices: [
          {
            value:TYPE_PROJECT ,
            name: '项目'
          },
          {
            name: '组件',
            value: TYPE_COMPONENT
          }
        ]
      }
    ])

    const titleTxt = type === TYPE_PROJECT ? '项目': '组件'

    this.template = this.template.filter(template => template.tag.includes(type))

    const questionArr = [
      {
       type: 'input',
       name: 'projectVersion',
       message: `请输入${titleTxt}版本号`,
       default: '1.0.0',
       validate: function (v) {
         // Declare function as asynchronous, and save the done callback
         const done = this.async();
         // Do async stuff
         setTimeout(function() {
           if (!semver.valid(v)) {
             // Pass the return value in the done callback
             done(`请输入合法的${titleTxt}版本号`);
           } else {
             // Pass the return value in the done callback
             done(null, true);
           }
         }, 1000);
       },
       filter: function (v) {
         if(semver.valid(v)) {
           return semver.valid(v)
         }
         return v
       }
     },{
       type: 'list',
       name: 'templateName',
       message: `请选择${titleTxt}模版`,
       choices: this.templateListSelect
     }
   ]
   if(!nameReg.test(this.projectName)) {
     questionArr.unshift({
       type: 'input',
       name: 'projectName',
       message: `请输入${titleTxt}名称`,
       default: '',
       validate: function(v) {
           // Declare function as asynchronous, and save the done callback
           const done = this.async();
           // Do async stuff
           setTimeout(function() {
             if (!nameReg.test(v)) {
               // Pass the return value in the done callback
               done(`请输入合法的${titleTxt}名称`);
             } else {
               // Pass the return value in the done callback
               done(null, true);
             }
           }, 1000);
       },
       filter: function(v) {
         return v
       }
     },)
   } else {
     projectInfo.projectName = this.projectName;
   }

    // if(type === TYPE_PROJECT) {

    // }
     if (type === TYPE_COMPONENT) {
      const descriptionPrompt = {
        type: 'input',
        name: 'componentDescription',
        message: '请输入组件描述信息',
        default: '',
        validate: function (v) {
          // Declare function as asynchronous, and save the done callback
          const done = this.async();
          // Do async stuff
          setTimeout(function() {
            if (!v) {
              // Pass the return value in the done callback
              done('组件信息不能为空');
            } else {
              // Pass the return value in the done callback
              done(null, true);
            }
          }, 1000);
        },
        filter: function (v) {
          if(semver.valid(v)) {
            return semver.valid(v)
          }
          return v
        }
      }
      
      questionArr.push(descriptionPrompt)
    }
    const o = await inquirer.prompt(questionArr)
    projectInfo = {
      ...projectInfo,
      ...o,
      type
    }
    // projectInfo.type = type;

    if(projectInfo.projectName) {
      projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '')
    }
    if(projectInfo.projectVersion) {
      projectInfo.version = projectInfo.projectVersion
    }
    console.log(projectInfo, 'projectInfo')
    // 4. 获取项目基本信息
    return projectInfo

  }

  get templateListSelect() {
    return this.template.map(item => {
      return {
        value: item.npmName,
        name: item.name
      }
    })
  }

  isCwdEmpty(nowPath) {
    let fileList = fs.readdirSync(nowPath);
    console.log(fileList, 'fileList')
    fileList = fileList.filter(item => (
      !item.startsWith('.') && item !== 'node_modules'
    ))
    return !fileList || fileList.length === 0
  }
}

module.exports = function(args) {
  return new InitCommand(args)
};